using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace AGame.TabOrm
{
    public class TabOrmBuilder
    {
        public static TabOrmBuilder Create()
        {
            return new TabOrmBuilder();
        }
        
        private readonly TabOrmObjConvert _objConvert = new TabOrmObjConvert();

        public TabOrmBuilder SetConvertFunc<T>(Func<string, object> strToObj, Func<object, string> objToStr)
        {
            return SetConvertFunc(typeof(T), strToObj, objToStr);
        }

        public TabOrmBuilder SetConvertFunc(Type type, Func<string, object> strToObj, Func<object, string> objToStr)
        {
            _objConvert.SetConvertFunc(type, strToObj, objToStr);
            return this;
        }

        public TabOrmObjConvert GetConvert()
        {
            return _objConvert;
        }

        public TabRepository<T, TKey> Build<T, TKey>()
        {
            var rep = new TabRepository<T, TKey> {ObjConvert = _objConvert};
            try
            {
                rep.BuildRepository();
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return null;
            }
            return rep;
        }
    }
    
    public class TabOrmManager
    {
#if UNITY_EDITOR
        [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
        static void RestStaticVariables()
        {
            _instance = null;
        }
#endif        
        private static TabOrmManager _instance;
        public static TabOrmManager Instance => _instance ?? (_instance = new TabOrmManager());
        private readonly Dictionary<Type, object> RepositoryDict = new Dictionary<Type, object>();

        private TabOrmManager()
        {
        }

        public void CleanRepository<T, TKey>()
        {
            var type = typeof(T);
            if (RepositoryDict.TryGetValue(type, out var repository) && repository is TabRepository<T, TKey> table)
            {
                table.Destroy();
                RepositoryDict.Remove(type);
            }
        }

        public void CleanAllRepository()
        {
            RepositoryDict.Clear();
        }

        /// <summary>
        /// 直接实现，建议调用
        /// </summary>
        /// <param name="objConvert"></param>
        /// <param name="refreshTab"></param>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <returns></returns>
        public TabRepository<T, TKey> GetRepository<T, TKey>(TabOrmObjConvert objConvert = null, bool refreshTab = false)
        {
            var type = typeof(T);
            if (RepositoryDict.TryGetValue(type, out var repository))
            {
                var r = repository as TabRepository<T, TKey>;
                if (refreshTab)
                {
                    r?.RefreshTab();
                }
                return r;
            }
            var rep = new TabRepository<T, TKey>();
            if (objConvert != null)
            {
                rep.ObjConvert = objConvert;
            }
            rep.BuildRepository();
            
            RepositoryDict[type] = rep;
            return rep;
        }

        public TabRepository<T, TKey> GetRepository<T, TKey>(bool refreshTab)
        {
            return GetRepository<T, TKey>(null, refreshTab);
        }
        
        /// <summary>
        /// 反射实现，不建议调用
        /// </summary>
        /// <param name="type"></param>
        /// <param name="keyType"></param>
        /// <param name="objConvert"></param>
        /// <returns></returns>
        public object GetRepository(Type type, Type keyType, TabOrmObjConvert objConvert = null)
        {
            if (RepositoryDict.TryGetValue(type, out var repository))
            {
                return repository;
            }
            var repositoryType = typeof(TabRepository<,>);
            repositoryType = repositoryType.MakeGenericType(type, keyType);
            var obj = Activator.CreateInstance(repositoryType);
            if (objConvert != null)
            {
                var fieldInfo = repositoryType.GetField("ObjConvert");
                fieldInfo?.SetValue(obj,objConvert);
            }
            var methodInfo = repositoryType.GetMethod("BuildRepository");
            methodInfo?.Invoke(obj, Array.Empty<object>());
            RepositoryDict[type] = obj;
            return obj;
        }
        
        
    }
}